Fix segfault on python 3.13. (Closes: #1092354)
authorAnton Gladky <gladk@debian.org>
Tue, 28 Jan 2025 20:40:46 +0000 (21:40 +0100)
committerAnton Gladky <gladk@debian.org>
Tue, 28 Jan 2025 20:40:46 +0000 (21:40 +0100)
debian/patches/130_fix_python_3.13.patch [new file with mode: 0644]
debian/patches/series

diff --git a/debian/patches/130_fix_python_3.13.patch b/debian/patches/130_fix_python_3.13.patch
new file mode 100644 (file)
index 0000000..31641fd
--- /dev/null
@@ -0,0 +1,1058 @@
+Description: Fix python 3.13 segfault
+Author: Anton Gladky <gladk@debian.org>
+Bug-Debian: https://bugs.debian.org/1092354
+Applied-Upstream: https://gitlab.kitware.com/vtk/vtk/-/merge_requests/11486 ; https://gitlab.kitware.com/vtk/vtk/-/merge_requests/11486
+Last-Update: 2025-01-28
+
+--- vtk9-9.3.0+dfsg1.orig/Utilities/PythonInterpreter/vtkPythonInterpreter.cxx
++++ vtk9-9.3.0+dfsg1/Utilities/PythonInterpreter/vtkPythonInterpreter.cxx
+@@ -96,6 +96,7 @@ wchar_t* vtk_Py_UTF8ToWide(const char* a
+   return result;
+ }
++#if PY_VERSION_HEX < 0x03080000
+ std::string vtk_Py_WideToUTF8(const wchar_t* arg)
+ {
+   std::string result;
+@@ -109,6 +110,7 @@ std::string vtk_Py_WideToUTF8(const wcha
+   return result;
+ }
++#endif
+ std::vector<vtkWeakPointer<vtkPythonInterpreter>>* GlobalInterpreters;
+ std::vector<std::string> PythonPaths;
+@@ -333,12 +335,21 @@ void SetupVTKPythonPaths(bool isolated)
+   if (vtklib.empty())
+   {
+     VTKPY_DEBUG_MESSAGE(
+-      "`GetVTKVersion` library couldn't be found. Will use `Py_GetProgramName` next.");
++      "`GetVTKVersion` library couldn't be found. Will use `sys.executable` next.");
+   }
+   if (vtklib.empty())
+   {
++#if PY_VERSION_HEX >= 0x03080000
++    vtkPythonScopeGilEnsurer gilEnsurer;
++    PyObject* executable_path = PySys_GetObject("executable");
++    if (executable_path != Py_None)
++    {
++      vtklib = PyUnicode_AsUTF8AndSize(executable_path, nullptr);
++    }
++#else
+     vtklib = vtk_Py_WideToUTF8(Py_GetProgramName());
++#endif
+   }
+   vtklib = systools::CollapseFullPath(vtklib);
+--- /dev/null
++++ vtk9-9.3.0+dfsg1/Utilities/PythonInterpreter/vtkPythonInterpreter.cxx.orig
+@@ -0,0 +1,933 @@
++// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
++// SPDX-License-Identifier: BSD-3-Clause
++#include "vtkPythonInterpreter.h"
++#include "vtkPython.h" // this must be the first include.
++
++#include "vtkBuild.h"
++#include "vtkCommand.h"
++#include "vtkLogger.h"
++#include "vtkNew.h"
++#include "vtkObjectFactory.h"
++#include "vtkOutputWindow.h"
++#include "vtkPythonStdStreamCaptureHelper.h"
++#include "vtkResourceFileLocator.h"
++#include "vtkVersion.h"
++#include "vtkWeakPointer.h"
++#include "vtksys/Encoding.h"
++
++#include <vtksys/Encoding.hxx>
++#include <vtksys/SystemInformation.hxx>
++#include <vtksys/SystemTools.hxx>
++
++#include <algorithm>
++#include <csignal>
++#include <memory>
++#include <sstream>
++#include <string>
++#include <vector>
++
++#if defined(_WIN32) && !defined(__CYGWIN__)
++#define VTK_PATH_SEPARATOR "\\"
++#else
++#define VTK_PATH_SEPARATOR "/"
++#endif
++
++#define VTKPY_DEBUG_MESSAGE(x)                                                                     \
++  vtkVLog(vtkLogger::ConvertToVerbosity(vtkPythonInterpreter::GetLogVerbosity()), x)
++#define VTKPY_DEBUG_MESSAGE_VV(x)                                                                  \
++  vtkVLog(vtkLogger::ConvertToVerbosity(vtkPythonInterpreter::GetLogVerbosity() + 1), x)
++
++#if defined(_WIN32) && !defined(__CYGWIN__) && defined(VTK_BUILD_SHARED_LIBS) &&                   \
++  PY_VERSION_HEX >= 0x03080000
++#define vtkPythonInterpreter_USE_DIRECTORY_COOKIE
++#endif
++
++namespace
++{
++
++template <class T>
++class PoolT
++{
++  std::vector<T*> Strings;
++
++public:
++  ~PoolT()
++  {
++    for (T* astring : this->Strings)
++    {
++      PyMem_RawFree(astring);
++    }
++  }
++
++  T* push_back(T* val)
++  {
++    this->Strings.push_back(val);
++    return val;
++  }
++
++  T* pop_last()
++  {
++    if (this->Strings.empty())
++    {
++      return nullptr;
++    }
++    T* last = *this->Strings.rbegin();
++    this->Strings.pop_back();
++    return last;
++  }
++};
++
++using StringPool = PoolT<char>;
++using WCharStringPool = PoolT<wchar_t>;
++
++wchar_t* vtk_Py_UTF8ToWide(const char* arg)
++{
++  wchar_t* result = nullptr;
++  if (arg != nullptr)
++  {
++    size_t length = vtksysEncoding_mbstowcs(nullptr, arg, 0);
++    if (length > 0)
++    {
++      result = (wchar_t*)PyMem_RawMalloc(sizeof(wchar_t) * (length + 1));
++      vtksysEncoding_mbstowcs(result, arg, length + 1);
++    }
++  }
++
++  return result;
++}
++
++std::string vtk_Py_WideToUTF8(const wchar_t* arg)
++{
++  std::string result;
++  size_t length = vtksysEncoding_wcstombs(nullptr, arg, 0);
++  if (length > 0)
++  {
++    std::vector<char> chars(length + 1);
++    vtksysEncoding_wcstombs(chars.data(), arg, length + 1);
++    result.assign(chars.data(), length);
++  }
++
++  return result;
++}
++
++std::vector<vtkWeakPointer<vtkPythonInterpreter>>* GlobalInterpreters;
++std::vector<std::string> PythonPaths;
++
++void NotifyInterpreters(unsigned long eventid, void* calldata = nullptr)
++{
++  std::vector<vtkWeakPointer<vtkPythonInterpreter>>::iterator iter;
++  for (iter = GlobalInterpreters->begin(); iter != GlobalInterpreters->end(); ++iter)
++  {
++    if (iter->GetPointer())
++    {
++      iter->GetPointer()->InvokeEvent(eventid, calldata);
++    }
++  }
++}
++
++inline void vtkPrependPythonPath(const char* pathtoadd)
++{
++  VTKPY_DEBUG_MESSAGE("adding module search path " << pathtoadd);
++  vtkPythonScopeGilEnsurer gilEnsurer;
++  PyObject* path = PySys_GetObject("path");
++  PyObject* newpath = PyUnicode_FromString(pathtoadd);
++
++  // avoid adding duplicate paths.
++  if (PySequence_Contains(path, newpath) == 0)
++  {
++    PyList_Insert(path, 0, newpath);
++  }
++  Py_DECREF(newpath);
++}
++}
++
++VTK_ABI_NAMESPACE_BEGIN
++// Schwarz counter idiom for GlobalInterpreters object
++static unsigned int vtkPythonInterpretersCounter;
++vtkPythonGlobalInterpreters::vtkPythonGlobalInterpreters()
++{
++  if (vtkPythonInterpretersCounter++ == 0)
++  {
++    GlobalInterpreters = new std::vector<vtkWeakPointer<vtkPythonInterpreter>>();
++  }
++}
++
++vtkPythonGlobalInterpreters::~vtkPythonGlobalInterpreters()
++{
++  if (--vtkPythonInterpretersCounter == 0)
++  {
++    delete GlobalInterpreters;
++    GlobalInterpreters = nullptr;
++  }
++}
++
++bool vtkPythonInterpreter::InitializedOnce = false;
++bool vtkPythonInterpreter::CaptureStdin = false;
++bool vtkPythonInterpreter::RedirectOutput = true;
++bool vtkPythonInterpreter::ConsoleBuffering = false;
++std::string vtkPythonInterpreter::StdErrBuffer;
++std::string vtkPythonInterpreter::StdOutBuffer;
++int vtkPythonInterpreter::LogVerbosity = vtkLogger::VERBOSITY_TRACE;
++
++#if PY_VERSION_HEX >= 0x03000000
++struct CharDeleter
++{
++  void operator()(wchar_t* str) { PyMem_RawFree(str); }
++};
++#endif
++
++vtkStandardNewMacro(vtkPythonInterpreter);
++//------------------------------------------------------------------------------
++vtkPythonInterpreter::vtkPythonInterpreter()
++{
++  GlobalInterpreters->push_back(this);
++}
++
++//------------------------------------------------------------------------------
++vtkPythonInterpreter::~vtkPythonInterpreter()
++{
++  // We need to check that GlobalInterpreters has not been deleted yet. It can be
++  // deleted prior to a call to this destructor if another static object with a
++  // reference to a vtkPythonInterpreter object deletes that object after
++  // GlobalInterpreters has been destructed. It all depends on the destruction order
++  // of the other static object and GlobalInterpreters.
++  if (!GlobalInterpreters)
++  {
++    return;
++  }
++  std::vector<vtkWeakPointer<vtkPythonInterpreter>>::iterator iter;
++  for (iter = GlobalInterpreters->begin(); iter != GlobalInterpreters->end(); ++iter)
++  {
++    if (*iter == this)
++    {
++      GlobalInterpreters->erase(iter);
++      break;
++    }
++  }
++}
++
++//------------------------------------------------------------------------------
++bool vtkPythonInterpreter::IsInitialized()
++{
++  return (Py_IsInitialized() != 0);
++}
++
++//------------------------------------------------------------------------------
++void vtkPythonInterpreter::PrintSelf(ostream& os, vtkIndent indent)
++{
++  this->Superclass::PrintSelf(os, indent);
++}
++
++//------------------------------------------------------------------------------
++bool vtkPythonInterpreter::Initialize(int initsigs /*=0*/)
++{
++  return vtkPythonInterpreter::InitializeWithArgs(initsigs, 0, nullptr);
++}
++
++#if PY_VERSION_HEX >= 0x03080000
++static WCharStringPool PythonProgramName;
++#endif
++
++//------------------------------------------------------------------------------
++// Ensure that Python is pre-initialized enough for VTK to do its
++// initialization. Must be called before any `PyMem_*` calls are made.
++static bool vtkPythonPreConfig()
++{
++  // Guard against doing this multiple times.
++  static bool done = false;
++  if (done)
++  {
++    return false;
++  }
++  done = true;
++
++#if PY_VERSION_HEX >= 0x03080000
++  PyStatus status;
++  PyPreConfig preconfig;
++  PyPreConfig_InitPythonConfig(&preconfig);
++
++  preconfig.allocator = PYMEM_ALLOCATOR_NOT_SET;
++  preconfig.utf8_mode = 1;
++
++  status = Py_PreInitialize(&preconfig);
++  if (PyStatus_Exception(status))
++  {
++    Py_ExitStatusException(status);
++  }
++
++  return preconfig.isolated;
++#else
++  return Py_FrozenFlag;
++#endif
++}
++
++//------------------------------------------------------------------------------
++namespace
++{
++/**
++ * Since vtkPythonInterpreter is often used outside CPython executable, e.g.
++ * vtkpython, the default logic to locate Python standard libraries used by
++ * Python (which depends on the executable path) may fail or pickup incorrect
++ * Python libs. This methods address the issue by setting program name to help
++ * guide Python's default prefix/exec_prefix searching logic.
++ */
++void SetupPythonPrefix(bool isolated)
++{
++  using systools = vtksys::SystemTools;
++
++  // Check if we're using an isolated Python.
++  if (isolated)
++  {
++    VTKPY_DEBUG_MESSAGE("Isolated Python detected; skipping setting up of program path.");
++    return;
++  }
++
++  std::string pythonlib = vtkGetLibraryPathForSymbol(Py_InitializeEx);
++  if (pythonlib.empty())
++  {
++    VTKPY_DEBUG_MESSAGE("static Python build or `Py_InitializeEx` library couldn't be found. "
++                        "Set `PYTHONHOME` if Python standard library fails to load.");
++    return;
++  }
++
++  const std::string newprogramname =
++    systools::GetFilenamePath(pythonlib) + VTK_PATH_SEPARATOR "vtkpython";
++  VTKPY_DEBUG_MESSAGE("calling vtkPythonInterpreter::SetProgramName("
++    << newprogramname << ") to aid in setup of Python prefix.");
++  vtkPythonInterpreter::SetProgramName(newprogramname.c_str());
++}
++
++#ifdef vtkPythonInterpreter_USE_DIRECTORY_COOKIE
++PyObject* DLLDirectoryCookie = nullptr;
++
++void CloseDLLDirectoryCookie()
++{
++  if (DLLDirectoryCookie)
++  {
++    if (PyObject_HasAttrString(DLLDirectoryCookie, "close"))
++    {
++      PyObject* result = PyObject_CallMethod(DLLDirectoryCookie, "close", nullptr);
++      Py_XDECREF(result);
++    }
++    Py_XDECREF(DLLDirectoryCookie);
++    DLLDirectoryCookie = nullptr;
++  }
++}
++#endif
++
++//------------------------------------------------------------------------------
++/**
++ * Add paths to VTK's Python modules.
++ */
++void SetupVTKPythonPaths(bool isolated)
++{
++  // Check if we're using an isolated Python.
++  if (isolated)
++  {
++    VTKPY_DEBUG_MESSAGE("Isolated Python detected; skipping setting up of `vtk` package.");
++    return;
++  }
++
++  using systools = vtksys::SystemTools;
++  std::string vtklib = vtkGetLibraryPathForSymbol(GetVTKVersion);
++  if (vtklib.empty())
++  {
++    VTKPY_DEBUG_MESSAGE(
++      "`GetVTKVersion` library couldn't be found. Will use `Py_GetProgramName` next.");
++  }
++
++  if (vtklib.empty())
++  {
++    vtklib = vtk_Py_WideToUTF8(Py_GetProgramName());
++  }
++
++  vtklib = systools::CollapseFullPath(vtklib);
++  const std::string vtkdir = systools::GetFilenamePath(vtklib);
++
++#if defined(_WIN32) && !defined(__CYGWIN__) && defined(VTK_BUILD_SHARED_LIBS)
++  // On Windows, based on how the executable is run, we end up failing to load
++  // pyd files due to inability to load dependent dlls. This seems to overcome
++  // the issue.
++  if (!vtkdir.empty())
++  {
++#if PY_VERSION_HEX >= 0x03080000
++    vtkPythonScopeGilEnsurer gilEnsurer(false, true);
++    CloseDLLDirectoryCookie();
++    PyObject* os = PyImport_ImportModule("os");
++    if (os)
++    {
++      PyObject* add_dll_directory = PyObject_GetAttrString(os, "add_dll_directory");
++      if (add_dll_directory && PyCallable_Check(add_dll_directory))
++      {
++        PyObject* newpath = PyUnicode_FromString(vtkdir.c_str());
++        DLLDirectoryCookie = PyObject_CallFunctionObjArgs(add_dll_directory, newpath, nullptr);
++        Py_XDECREF(newpath);
++      }
++
++      Py_XDECREF(add_dll_directory);
++    }
++
++    Py_XDECREF(os);
++#else
++    std::string env_path;
++    if (systools::GetEnv("PATH", env_path))
++    {
++      env_path = vtkdir + ";" + env_path;
++    }
++    else
++    {
++      env_path = vtkdir;
++    }
++    systools::PutEnv(std::string("PATH=") + env_path);
++#endif
++  }
++#endif
++
++#if defined(VTK_BUILD_SHARED_LIBS)
++  vtkPythonInterpreter::PrependPythonPath(vtkdir.c_str(), "vtkmodules/__init__.py");
++#else
++  // since there may be other packages not zipped (e.g. mpi4py), we added path to _vtk.zip
++  // to the search path as well.
++  vtkPythonInterpreter::PrependPythonPath(vtkdir.c_str(), "_vtk.zip", /*add_landmark*/ false);
++  vtkPythonInterpreter::PrependPythonPath(vtkdir.c_str(), "_vtk.zip", /*add_landmark*/ true);
++#endif
++}
++}
++
++//------------------------------------------------------------------------------
++bool vtkPythonInterpreter::InitializeWithArgs(int initsigs, int argc, char* argv[])
++{
++  bool isolated = vtkPythonPreConfig();
++
++  if (Py_IsInitialized() == 0)
++  {
++    // guide the mechanism to locate Python standard library, if possible.
++    SetupPythonPrefix(isolated);
++    bool signals_installed = initsigs != 0;
++
++    // Need two copies of args, because programs might modify the first
++    using OwnedWideString = std::unique_ptr<wchar_t, CharDeleter>;
++    std::vector<wchar_t*> argvForPython;
++    std::vector<OwnedWideString> argvCleanup;
++    for (int i = 0; i < argc; i++)
++    {
++      OwnedWideString argCopy(vtk_Py_UTF8ToWide(argv[i]), CharDeleter());
++      if (argCopy == nullptr)
++      {
++        fprintf(stderr,
++          "Fatal vtkpython error: "
++          "unable to decode the command line argument #%i\n",
++          i + 1);
++        return false;
++      }
++
++      argvForPython.push_back(argCopy.get());
++      argvCleanup.emplace_back(std::move(argCopy));
++    }
++    argvForPython.push_back(nullptr);
++
++#if PY_VERSION_HEX < 0x03080000
++    Py_InitializeEx(initsigs);
++    // setup default argv. Without this, code snippets that check `sys.argv` may
++    // fail when run in embedded VTK Python environment.
++    PySys_SetArgvEx(argc, argvForPython.data(), 0);
++
++    isolated = Py_FrozenFlag;
++#else
++    PyConfig config;
++    PyStatus status;
++    PyConfig_InitPythonConfig(&config);
++    config.install_signal_handlers = initsigs;
++    config.program_name = PythonProgramName.pop_last();
++    status = PyConfig_SetArgv(&config, argc, argvForPython.data());
++    if (PyStatus_IsError(status))
++    {
++      PyConfig_Clear(&config);
++      return false;
++    }
++
++    status = Py_InitializeFromConfig(&config);
++    if (PyStatus_IsError(status))
++    {
++      PyConfig_Clear(&config);
++      return false;
++    }
++    isolated = config.pathconfig_warnings == 0;
++    PyConfig_Clear(&config);
++#endif
++
++#ifdef VTK_PYTHON_FULL_THREADSAFE
++#if PY_VERSION_HEX < 0x03090000
++    // In Python 3.9 and higher, PyEval_ThreadsInitialized() and
++    // PyEval_InitThreads() are deprecated and do nothing.
++    // GIL initialization is handled by Py_InitializeEx().
++    int threadInit = PyEval_ThreadsInitialized();
++    if (!threadInit)
++    {
++      PyEval_InitThreads(); // initialize and acquire GIL
++    }
++#endif
++    // Always release GIL, as it has been acquired either by PyEval_InitThreads
++    // prior to Python 3.7 or by Py_InitializeEx in Python 3.7 and after
++    PyEval_SaveThread();
++#endif
++
++#ifdef SIGINT
++    if (signals_installed)
++    {
++      // Put default SIGINT handler back after Py_Initialize/Py_InitializeEx.
++      signal(SIGINT, SIG_DFL);
++    }
++#endif
++  }
++
++  if (!vtkPythonInterpreter::InitializedOnce)
++  {
++    vtkPythonInterpreter::InitializedOnce = true;
++
++    // HACK: Calling PyRun_SimpleString for the first time for some reason results in
++    // a "\n" message being generated which is causing the error dialog to
++    // popup. So we flush that message out of the system before setting up the
++    // callbacks.
++    vtkPythonInterpreter::RunSimpleString("");
++
++    // Redirect Python's stdout and stderr and stdin - GIL protected operation
++    if (vtkPythonInterpreter::RedirectOutput)
++    {
++      // Setup handlers for stdout/stdin/stderr.
++      vtkPythonStdStreamCaptureHelper* wrapperOut = NewPythonStdStreamCaptureHelper(false);
++      vtkPythonStdStreamCaptureHelper* wrapperErr = NewPythonStdStreamCaptureHelper(true);
++      vtkPythonScopeGilEnsurer gilEnsurer;
++      PySys_SetObject("stdout", reinterpret_cast<PyObject*>(wrapperOut));
++      PySys_SetObject("stderr", reinterpret_cast<PyObject*>(wrapperErr));
++      PySys_SetObject("stdin", reinterpret_cast<PyObject*>(wrapperOut));
++      Py_DECREF(wrapperOut);
++      Py_DECREF(wrapperErr);
++    }
++
++    // We call this before processing any of Python paths added by the
++    // application using `PrependPythonPath`. This ensures that application
++    // specified paths are preferred to the ones `vtkPythonInterpreter` adds.
++    SetupVTKPythonPaths(isolated);
++
++    for (size_t cc = 0; cc < PythonPaths.size(); cc++)
++    {
++      vtkPrependPythonPath(PythonPaths[cc].c_str());
++    }
++
++    NotifyInterpreters(vtkCommand::EnterEvent);
++    return true;
++  }
++
++  return false;
++}
++
++//------------------------------------------------------------------------------
++void vtkPythonInterpreter::Finalize()
++{
++  if (Py_IsInitialized() != 0)
++  {
++    NotifyInterpreters(vtkCommand::ExitEvent);
++    vtkPythonScopeGilEnsurer gilEnsurer(false, true);
++#ifdef vtkPythonInterpreter_USE_DIRECTORY_COOKIE
++    CloseDLLDirectoryCookie();
++#endif
++    // Py_Finalize will take care of releasing gil
++    Py_Finalize();
++  }
++}
++
++//------------------------------------------------------------------------------
++void vtkPythonInterpreter::SetProgramName(const char* programname)
++{
++  vtkPythonPreConfig();
++  if (programname)
++  {
++#if PY_VERSION_HEX >= 0x03080000
++    if (wchar_t* argv0 = vtk_Py_UTF8ToWide(programname))
++    {
++      PythonProgramName.push_back(argv0);
++    }
++    else
++    {
++      fprintf(stderr,
++        "Fatal vtkpython error: "
++        "unable to decode the program name\n");
++      wchar_t* empty = (wchar_t*)PyMem_RawMalloc(sizeof(wchar_t));
++      empty[0] = 0;
++      PythonProgramName.push_back(empty);
++    }
++#else
++    // From Python Docs: The argument should point to a zero-terminated character
++    // string in static storage whose contents will not change for the duration of
++    // the program's execution. No code in the Python interpreter will change the
++    // contents of this storage.
++    wchar_t* argv0 = vtk_Py_UTF8ToWide(programname);
++    if (argv0 == nullptr)
++    {
++      fprintf(stderr,
++        "Fatal vtkpython error: "
++        "unable to decode the program name\n");
++      static wchar_t empty[1] = { 0 };
++      argv0 = empty;
++      Py_SetProgramName(argv0);
++    }
++    else
++    {
++      static WCharStringPool wpool;
++      Py_SetProgramName(wpool.push_back(argv0));
++    }
++#endif
++  }
++}
++
++//------------------------------------------------------------------------------
++void vtkPythonInterpreter::PrependPythonPath(const char* dir)
++{
++  if (!dir)
++  {
++    return;
++  }
++
++  std::string out_dir = dir;
++
++#if defined(_WIN32) && !defined(__CYGWIN__)
++  // Convert slashes for this platform.
++  std::replace(out_dir.begin(), out_dir.end(), '/', '\\');
++#endif
++
++  if (Py_IsInitialized() == 0)
++  {
++    // save path for future use.
++    PythonPaths.push_back(out_dir);
++    return;
++  }
++
++  // Append the path to the python sys.path object.
++  vtkPrependPythonPath(out_dir.c_str());
++}
++
++//------------------------------------------------------------------------------
++void vtkPythonInterpreter::PrependPythonPath(
++  const char* anchor, const char* landmark, bool add_landmark)
++{
++  const std::vector<std::string> prefixes = {
++    VTK_PYTHON_SITE_PACKAGES_SUFFIX
++#if defined(__APPLE__)
++    // if in an App bundle, the `sitepackages` dir is <app_root>/Contents/Python
++    ,
++    "Contents/Python"
++#endif
++    ,
++    "."
++  };
++
++  vtkNew<vtkResourceFileLocator> locator;
++  locator->SetLogVerbosity(vtkPythonInterpreter::GetLogVerbosity() + 1);
++  std::string path = locator->Locate(anchor, prefixes, landmark);
++  if (!path.empty())
++  {
++    if (add_landmark)
++    {
++      path = path + "/" + landmark;
++    }
++    vtkPythonInterpreter::PrependPythonPath(path.c_str());
++  }
++}
++
++//------------------------------------------------------------------------------
++int vtkPythonInterpreter::PyMain(int argc, char** argv)
++{
++  vtksys::SystemTools::EnableMSVCDebugHook();
++
++  int count_v = 0;
++  for (int cc = 0; cc < argc; ++cc)
++  {
++    if (argv[cc] && strcmp(argv[cc], "-v") == 0)
++    {
++      ++count_v;
++    }
++    if (argv[cc] && strcmp(argv[cc], "-vv") == 0)
++    {
++      count_v += 2;
++    }
++  }
++
++  if (count_v > 0)
++  {
++    // change the vtkPythonInterpreter's log verbosity. We only touch it
++    // if the command line arguments explicitly requested a certain verbosity.
++    vtkPythonInterpreter::SetLogVerbosity(vtkLogger::VERBOSITY_INFO);
++    vtkLogger::SetStderrVerbosity(vtkLogger::ConvertToVerbosity(count_v - 1));
++  }
++
++  vtkLogger::Init(argc, argv, nullptr); // since `-v` and `-vv` are parsed as Python verbosity flags
++                                        // and not log verbosity flags.
++
++  // Need two copies of args, because the first array may be modified elsewhere.
++  using OwnedCString = std::unique_ptr<char, decltype(&std::free)>;
++  std::vector<char*> argvForPython;
++  std::vector<OwnedCString> argvCleanup;
++  for (int i = 0; i < argc; i++)
++  {
++    if (!argv[i])
++    {
++      continue;
++    }
++    if (strcmp(argv[i], "--enable-bt") == 0)
++    {
++      vtksys::SystemInformation::SetStackTraceOnError(1);
++      continue;
++    }
++    if (strcmp(argv[i], "-V") == 0)
++    {
++      // print out VTK version and let argument pass to Py_RunMain(). At which
++      // point, Python will print its version and exit.
++      cout << vtkVersion::GetVTKSourceVersion() << endl;
++    }
++
++    OwnedCString argCopy(strdup(argv[i]), &std::free);
++    if (argCopy == nullptr)
++    {
++      fprintf(stderr,
++        "Fatal vtkpython error: "
++        "unable to copy the command line argument #%i\n",
++        i + 1);
++      return 1;
++    }
++
++    argvForPython.push_back(argCopy.get());
++    argvCleanup.emplace_back(std::move(argCopy));
++  }
++  int argvForPythonSize = static_cast<int>(argvForPython.size());
++  argvForPython.push_back(nullptr);
++
++  vtkPythonInterpreter::InitializeWithArgs(1, argvForPythonSize, argvForPython.data());
++
++#if PY_VERSION_HEX >= 0x03070000 && PY_VERSION_HEX < 0x03080000
++  // Python 3.7.0 has a bug where Py_InitializeEx (called above) followed by
++  // Py_Main (at the end of this block) causes a crash. Gracefully exit with
++  // failure if we're using 3.7.0 and suggest getting the newest 3.7.x release.
++  // See <https://gitlab.kitware.com/vtk/vtk/-/issues/17434> for details.
++  {
++    bool is_ok = true;
++    vtkPythonScopeGilEnsurer gilEnsurer(false, true);
++    PyObject* sys = PyImport_ImportModule("sys");
++    if (sys)
++    {
++      // XXX: Check sys.implementation.name == 'cpython'?
++
++      PyObject* version_info = PyObject_GetAttrString(sys, "version_info");
++      if (version_info)
++      {
++        PyObject* major = PyObject_GetAttrString(version_info, "major");
++        PyObject* minor = PyObject_GetAttrString(version_info, "minor");
++        PyObject* micro = PyObject_GetAttrString(version_info, "micro");
++
++        auto py_number_cmp = [](PyObject* obj, long expected) {
++          return obj && PyLong_Check(obj) && PyLong_AsLong(obj) == expected;
++        };
++
++        // Only 3.7.0 has this issue. Any failures to get the version
++        // information is OK; we'll just crash later anyways if the version is
++        // bad.
++        is_ok = !py_number_cmp(major, 3) || !py_number_cmp(minor, 7) || !py_number_cmp(micro, 0);
++
++        Py_XDECREF(micro);
++        Py_XDECREF(minor);
++        Py_XDECREF(major);
++      }
++
++      Py_XDECREF(version_info);
++    }
++
++    Py_XDECREF(sys);
++
++    if (!is_ok)
++    {
++      std::cerr << "Python 3.7.0 has a known issue that causes a crash with a "
++                   "specific API usage pattern. This has been fixed in 3.7.1 and all "
++                   "newer 3.7.x Python releases. Exiting now to avoid the crash."
++                << std::endl;
++      return 1;
++    }
++  }
++#endif
++
++#if PY_VERSION_HEX < 0x03080000
++  // Need two copies of args, because programs might modify the first
++  using OwnedWideString = std::unique_ptr<wchar_t, CharDeleter>;
++  std::vector<wchar_t*> argvForPythonWide;
++  std::vector<OwnedWideString> argvCleanupWide;
++  for (size_t i = 0; i < argvCleanup.size(); i++)
++  {
++    OwnedWideString argCopy(vtk_Py_UTF8ToWide(argvCleanup[i].get()), CharDeleter());
++    if (argCopy == nullptr)
++    {
++      fprintf(stderr,
++        "Fatal vtkpython error: "
++        "unable to decode the command line argument #%zu\n",
++        i + 1);
++      return 1;
++    }
++
++    argvForPythonWide.push_back(argCopy.get());
++    argvCleanupWide.emplace_back(std::move(argCopy));
++  }
++  int argvForPythonWideSize = static_cast<int>(argvForPythonWide.size());
++  argvForPythonWide.push_back(nullptr);
++
++  vtkPythonScopeGilEnsurer gilEnsurer(false, true);
++  return Py_Main(argvForPythonWideSize, argvForPythonWide.data());
++#else
++  vtkPythonScopeGilEnsurer gilEnsurer(false, true);
++  return Py_RunMain();
++#endif
++}
++
++//------------------------------------------------------------------------------
++int vtkPythonInterpreter::RunSimpleString(const char* script)
++{
++  vtkPythonInterpreter::Initialize(1);
++  vtkPythonInterpreter::ConsoleBuffering = true;
++
++  // The embedded python interpreter cannot handle DOS line-endings, see
++  // http://sourceforge.net/tracker/?group_id=5470&atid=105470&func=detail&aid=1167922
++  std::string buffer = script ? script : "";
++  buffer.erase(std::remove(buffer.begin(), buffer.end(), '\r'), buffer.end());
++
++  // The cast is necessary because PyRun_SimpleString() hasn't always been const-correct
++  int pyReturn;
++  {
++    vtkPythonScopeGilEnsurer gilEnsurer;
++    pyReturn = PyRun_SimpleString(buffer.c_str());
++  }
++
++  vtkPythonInterpreter::ConsoleBuffering = false;
++  if (!vtkPythonInterpreter::StdErrBuffer.empty())
++  {
++    vtkOutputWindow::GetInstance()->DisplayErrorText(vtkPythonInterpreter::StdErrBuffer.c_str());
++    NotifyInterpreters(
++      vtkCommand::ErrorEvent, const_cast<char*>(vtkPythonInterpreter::StdErrBuffer.c_str()));
++    vtkPythonInterpreter::StdErrBuffer.clear();
++  }
++  if (!vtkPythonInterpreter::StdOutBuffer.empty())
++  {
++    vtkOutputWindow::GetInstance()->DisplayText(vtkPythonInterpreter::StdOutBuffer.c_str());
++    NotifyInterpreters(
++      vtkCommand::SetOutputEvent, const_cast<char*>(vtkPythonInterpreter::StdOutBuffer.c_str()));
++    vtkPythonInterpreter::StdOutBuffer.clear();
++  }
++
++  return pyReturn;
++}
++
++//------------------------------------------------------------------------------
++void vtkPythonInterpreter::SetCaptureStdin(bool val)
++{
++  vtkPythonInterpreter::CaptureStdin = val;
++}
++
++//------------------------------------------------------------------------------
++bool vtkPythonInterpreter::GetCaptureStdin()
++{
++  return vtkPythonInterpreter::CaptureStdin;
++}
++
++//------------------------------------------------------------------------------
++void vtkPythonInterpreter::SetRedirectOutput(bool redirect)
++{
++  vtkPythonInterpreter::RedirectOutput = redirect;
++}
++
++//------------------------------------------------------------------------------
++bool vtkPythonInterpreter::GetRedirectOutput()
++{
++  return vtkPythonInterpreter::RedirectOutput;
++}
++
++//------------------------------------------------------------------------------
++void vtkPythonInterpreter::WriteStdOut(const char* txt)
++{
++  if (vtkPythonInterpreter::ConsoleBuffering)
++  {
++    vtkPythonInterpreter::StdOutBuffer += std::string(txt);
++  }
++  else
++  {
++    vtkOutputWindow::GetInstance()->DisplayText(txt);
++    NotifyInterpreters(vtkCommand::SetOutputEvent, const_cast<char*>(txt));
++  }
++}
++
++//------------------------------------------------------------------------------
++void vtkPythonInterpreter::FlushStdOut() {}
++
++//------------------------------------------------------------------------------
++void vtkPythonInterpreter::WriteStdErr(const char* txt)
++{
++  if (vtkPythonInterpreter::ConsoleBuffering)
++  {
++    vtkPythonInterpreter::StdErrBuffer += std::string(txt);
++  }
++  else
++  {
++    vtkOutputWindow::GetInstance()->DisplayErrorText(txt);
++    NotifyInterpreters(vtkCommand::ErrorEvent, const_cast<char*>(txt));
++  }
++}
++
++//------------------------------------------------------------------------------
++void vtkPythonInterpreter::FlushStdErr() {}
++
++//------------------------------------------------------------------------------
++vtkStdString vtkPythonInterpreter::ReadStdin()
++{
++  if (!vtkPythonInterpreter::CaptureStdin)
++  {
++    vtkStdString string;
++    cin >> string;
++    return string;
++  }
++  vtkStdString string;
++  NotifyInterpreters(vtkCommand::UpdateEvent, &string);
++  return string;
++}
++
++//------------------------------------------------------------------------------
++void vtkPythonInterpreter::SetLogVerbosity(int val)
++{
++  vtkPythonInterpreter::LogVerbosity = vtkLogger::ConvertToVerbosity(val);
++}
++
++//------------------------------------------------------------------------------
++int vtkPythonInterpreter::GetLogVerbosity()
++{
++  return vtkPythonInterpreter::LogVerbosity;
++}
++
++#if defined(_WIN32)
++//------------------------------------------------------------------------------
++vtkWideArgsConverter::vtkWideArgsConverter(int argc, wchar_t* wargv[])
++{
++  this->Argc = argc;
++  for (int i = 0; i < argc; i++)
++  {
++    std::string str = vtksys::Encoding::ToNarrow(wargv[i]);
++    char* cstr = vtksys::SystemTools::DuplicateString(str.c_str());
++    Args.push_back(cstr);
++    MemCache.push_back(cstr);
++  }
++  Args.push_back(nullptr);
++}
++
++//------------------------------------------------------------------------------
++vtkWideArgsConverter::~vtkWideArgsConverter()
++{
++  for (auto cstr : MemCache)
++  {
++    delete[] cstr;
++  }
++}
++#endif
++VTK_ABI_NAMESPACE_END
+--- vtk9-9.3.0+dfsg1.orig/Utilities/PythonInterpreter/vtkPythonStdStreamCaptureHelper.h
++++ vtk9-9.3.0+dfsg1/Utilities/PythonInterpreter/vtkPythonStdStreamCaptureHelper.h
+@@ -16,7 +16,8 @@ VTK_ABI_NAMESPACE_BEGIN
+ struct vtkPythonStdStreamCaptureHelper
+ {
+   PyObject_HEAD
+-  int softspace; // Used by print to keep track of its state.
++  int softspace;        // Used by print to keep track of its state.
++  const char* Encoding; // Encoding, set to "utf-8"
+   bool DumpToError;
+   void Write(const char* string)
+@@ -84,6 +85,9 @@ static PyMemberDef vtkPythonStdStreamCap
+   { VTK_PYTHON_MEMBER_DEF_STR("softspace"), T_INT,
+     offsetof(vtkPythonStdStreamCaptureHelper, softspace), 0,
+     VTK_PYTHON_MEMBER_DEF_STR("Placeholder so print can keep state.") },
++  { VTK_PYTHON_MEMBER_DEF_STR("encoding"), T_STRING,
++    offsetof(vtkPythonStdStreamCaptureHelper, Encoding), READONLY,
++    VTK_PYTHON_MEMBER_DEF_STR("Text encoding for file.") },
+   { nullptr, 0, 0, 0, nullptr }
+ };
+@@ -250,6 +254,7 @@ static vtkPythonStdStreamCaptureHelper*
+     PyObject_New(vtkPythonStdStreamCaptureHelper, &vtkPythonStdStreamCaptureHelperType);
+   if (wrapper)
+   {
++    wrapper->Encoding = "utf-8";
+     wrapper->DumpToError = for_stderr;
+   }
+--- vtk9-9.3.0+dfsg1.orig/Wrapping/Python/vtkmodules/test/Testing.py
++++ vtk9-9.3.0+dfsg1/Wrapping/Python/vtkmodules/test/Testing.py
+@@ -515,8 +515,10 @@ def test(cases):
+     """
+     # Make the test suites from the arguments.
+     suites = []
+-    for case in cases:
+-        suites.append(unittest.makeSuite(case[0], case[1]))
++    loader = unittest.TestLoader()
++    # the "name" is ignored (it was always just 'test')
++    for test,name in cases:
++        suites.append(loader.loadTestsFromTestCase(test))
+     test_suite = unittest.TestSuite(suites)
+     # Now run the tests.
+--- vtk9-9.3.0+dfsg1.orig/Wrapping/PythonCore/PyVTKNamespace.cxx
++++ vtk9-9.3.0+dfsg1/Wrapping/PythonCore/PyVTKNamespace.cxx
+@@ -112,8 +112,10 @@ PyObject* PyVTKNamespace_New(const char*
+   {
+     // make sure python has readied the type object
+     PyType_Ready(&PyVTKNamespace_Type);
+-    // call the allocator provided by python for this type
+-    self = PyVTKNamespace_Type.tp_alloc(&PyVTKNamespace_Type, 0);
++    // call the superclass new function
++    PyObject* empty = PyTuple_New(0);
++    self = PyVTKNamespace_Type.tp_base->tp_new(&PyVTKNamespace_Type, empty, nullptr);
++    Py_DECREF(empty);
+     // call the superclass init function
+     PyObject* pyname = PyUnicode_FromString(name);
+     PyObject* args = PyTuple_Pack(1, pyname);
+--- vtk9-9.3.0+dfsg1.orig/Wrapping/PythonCore/PyVTKTemplate.cxx
++++ vtk9-9.3.0+dfsg1/Wrapping/PythonCore/PyVTKTemplate.cxx
+@@ -761,8 +761,10 @@ PyObject* PyVTKTemplate_New(const char*
+ {
+   // make sure python has readied the type object
+   PyType_Ready(&PyVTKTemplate_Type);
+-  // call the allocator provided by python for this type
+-  PyObject* self = PyVTKTemplate_Type.tp_alloc(&PyVTKTemplate_Type, 0);
++  // call the superclass new function
++  PyObject* empty = PyTuple_New(0);
++  PyObject* self = PyVTKTemplate_Type.tp_base->tp_new(&PyVTKTemplate_Type, empty, nullptr);
++  Py_DECREF(empty);
+   // call the superclass init function
+   PyObject* pyname = PyUnicode_FromString(name);
+   PyObject* pydoc = PyUnicode_FromString(docstring);
index 0bb503d3ebe19dd874ebb602adad4a163a7b34fd..5e65f978d677bbb5bd9e97d05754c623787fe9e3 100644 (file)
@@ -12,3 +12,4 @@
 99_fix_ftbfs.patch
 100_add_missing_gl_header.patch
 121_add_support_for_loong64.patch
+130_fix_python_3.13.patch